package micro.beauty.common.mvc.filter;

import lombok.extern.slf4j.Slf4j;
import micro.beauty.common.mvc.wrapper.RequestWrapper;
import micro.beauty.common.mvc.wrapper.ResponseWrapper;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

/**
 * @Author: liuliya
 * @CreateDate: 2020/4/28 23:30
 */
@Slf4j
public class LoggingFilter extends OncePerRequestFilter {
    private static final String REQUEST_PREFIX_NAME = "Request Content: ";
    private static final String RESPONSE_PREFIX_NAME = "Response Content: ";
    private boolean enabledLogger;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if (enabledLogger) {
            request = new RequestWrapper(request);
            response = new ResponseWrapper(response);

            filterChain.doFilter(request, response);
            if (request.getServletPath().startsWith("/")) {
                printRequestLog(request);
                printResponseLog((ResponseWrapper) response);
            }
        } else {
            filterChain.doFilter(request, response);
        }

    }

    private void printRequestLog(final HttpServletRequest request) {
        StringBuilder msg = new StringBuilder();
        msg.append(REQUEST_PREFIX_NAME);
        HttpSession session = request.getSession(false);
        String method = request.getMethod();
        String contentType = request.getContentType();
        String requestURI = request.getRequestURI();
        String queryString = request.getQueryString();

        if (!ObjectUtils.isEmpty(session)) {
            msg.append("SessionId = ")
                    .append(session.getId()).append(";\n\r");
        }
        if (!ObjectUtils.isEmpty(method)) {
            msg.append("Method = ")
                    .append(request.getMethod()).append(";\n\r");
        }
        if (!ObjectUtils.isEmpty(contentType)) {
            msg.append("ContentType = ")
                    .append(request.getContentType()).append(";\n\r");
        }
        msg.append("Uri = ").append(requestURI);

        if (!ObjectUtils.isEmpty(queryString)) {
            msg.append('?').append(request.getQueryString());
        }

        if (request instanceof RequestWrapper && !isMultipart(request) && !isBinaryContent(request)) {
            RequestWrapper requestWrapper = (RequestWrapper) request;
            String requestBodyString = requestWrapper.getRequestBodyString(request);
            if (!StringUtils.isEmpty(requestBodyString)) {
                msg.append("; Payload = ").append(requestBodyString);
            }
        }
        log.info(msg.toString());
    }

    private boolean isBinaryContent(final HttpServletRequest request) {
        if (StringUtils.isEmpty(request.getContentType())) {
            return false;
        }
        return request.getContentType().startsWith("image")
                || request.getContentType().startsWith("video")
                || request.getContentType().startsWith("audio");
    }

    private boolean isMultipart(final HttpServletRequest request) {
        return !StringUtils.isEmpty(request.getContentType())
                && request.getContentType().startsWith("multipart/form-data");
    }

    private void printResponseLog(final ResponseWrapper response) {
        StringBuilder msg = new StringBuilder();
        msg.append(RESPONSE_PREFIX_NAME);
        try {
            msg.append("; payload = ")
                    .append(new String(response.toByteArray(), "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            log.warn("Failed to parse response payload", e);
        }
        log.info(msg.toString());
    }

    public LoggingFilter setEnabledLogger(boolean enabledLogger) {
        this.enabledLogger = enabledLogger;
        return this;
    }
}
